package action;

import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.sql.Timestamp;
import java.util.Formatter;
import java.util.UUID;

import org.json.JSONObject;

import bean.WechatCache;
import service.WechatCacheService;
import utils.AjaxResult;
import utils.AppConfig;
import utils.DateTimeUtils;
import utils.HttpsRequest;
import utils.SHA1;

public class WechatJsApi extends BaseAction {

    private String url;
    
    public static final String REQUEST_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
    
    public static boolean CACHED = false;
    public static String CACHED_JSAPI_TICKET = null;
    public static Timestamp JSAPI_TICKET_EXPIRES = null;
    
    private WechatCacheService wechatCacheService;
    private WechatAccessTokenApi wechatAccessTokenApi;
    
    public void setWechatAccessTokenApi(WechatAccessTokenApi wechatAccessTokenApi) {
        this.wechatAccessTokenApi = wechatAccessTokenApi;
    }
    
    public void setWechatCacheService(WechatCacheService wechatCacheService) {
        this.wechatCacheService = wechatCacheService;
    }
    
    /**
     * JS-API config
     * Get the signature and return related information back
     * @return
     */
    public String config() {
        //TODO 测试，待删除
        //url = "www.imdcfz.com/wxtest/index.html";
        
        AjaxResult result = null;
        if (isNullOrEmptyString(url)) {
            result = new AjaxResult(AjaxResult.CODE_ERROR, "缺少参数url");
        } else {
            url = url.trim().split("#")[0];
            String ticket = getJsapiTicket();
            String timestamp = create_timestamp();
            System.out.println("config timestamp = "+timestamp);
            String nonceStr = create_nonce_str().substring(0,31);
            System.out.println("config nonceStr = "+nonceStr);
            
            // joint
            StringBuilder builder = new StringBuilder();
            builder.append("jsapi_ticket=").append(ticket)
                    .append("&noncestr=").append(nonceStr)
                    .append("&timestamp=").append(timestamp)
                    .append("&url=").append(url);

            String signature = new SHA1().getDigestOfString(builder.toString().getBytes()).toLowerCase();

            result = new AjaxResult(AjaxResult.CODE_SUCCESS, "success");
            result.addData("appId", AppConfig.APP_ID);
            result.addData("timestamp", timestamp);
            result.addData("nonceStr", nonceStr);
            result.addData("signature", signature);
            
        }
        
        try {
            inputStream = new ByteArrayInputStream(result.toString().getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        
        return SUCCESS;
    }
    
    /**
     * Get Jsapi ticket
     * @return
     */
    public String getJsapiTicket() {
        // Attempt to get jsapi ticket from cache
        String ticket = getJsapiTicketFromCache();
        if (ticket != null) {
            return ticket;
        }
        
        // attempt to get jsapi ticket from database
        ticket = getJsapiTicketFromDatabase();
        if (ticket != null) {
            return ticket;
        }
        
        // request jsapi ticket from wechat server
        ticket = getJsapiTicketFromWechatServer();
        return ticket;
    }

    /**
     * Attempt to get jsapi ticket from database
     * @return
     */
    private String getJsapiTicketFromDatabase() {
        // get last cache from database
        WechatCache cache = wechatCacheService.getLast();
        
        // if the ticket has been cached, token and expires are exist
        if (cache != null && cache.getJsapiTicket() != null && cache.getJsapiTicketExpires() != null) {
            // and the cached token does not expires
            Timestamp now = DateTimeUtils.generateTimestamp();
            if (cache.getJsapiTicketExpires().after(now)) {
                // update the ROM cache
                CACHED = false;
                CACHED_JSAPI_TICKET = cache.getJsapiTicket();
                JSAPI_TICKET_EXPIRES = new Timestamp(cache.getJsapiTicketExpires().getTime());
                CACHED = true;
                
                // return the token
                return cache.getJsapiTicket();
            }
        }
        
        return null;
    }

    /**
     * Attempt to get jsapi ticket from cache
     * @return
     */
    private String getJsapiTicketFromCache() {
        // the jsapi ticket has been cached, token and expires are exist
        if (CACHED && CACHED_JSAPI_TICKET != null && JSAPI_TICKET_EXPIRES != null) {
            // and the cached ticket does not expires
            Timestamp now = DateTimeUtils.generateTimestamp();
            if (JSAPI_TICKET_EXPIRES.after(now)) {
                return CACHED_JSAPI_TICKET;
            }
        }
        
        // otherwise
        CACHED = false;
        return null;
    }

    /**
     * Request access token from wechat server
     * @return
     */
    private String getJsapiTicketFromWechatServer() {
        String token = wechatAccessTokenApi.getAccessToken();
        if (token == null) {
            return null;
        }
        
        String ticket = null;
        
        // 构造参数
        StringBuilder builder = new StringBuilder();
        builder.append("access_token=").append(token.trim()).append("&type=jsapi");
        
        // 请求时间
        Timestamp now = DateTimeUtils.generateTimestamp();
        
        // 获取返回结果
        String response = null;
        try {
            response = HttpsRequest.sendGet(REQUEST_JSAPI_TICKET_URL, builder.toString());
        } catch (Exception e) {
            response = null;
        }
        
        // 处理返回结果
        if (response == null) {
            // 获取失败
            ticket = null;
        } else {
            // 解析返回数据
            JSONObject obj = new JSONObject(response);
            if (obj.has("ticket") && obj.has("expires_in")) {
                // 获取成功
                ticket = obj.getString("ticket");
                int expires = obj.getInt("expires_in");
                
                // 更新ROM Cache
                CACHED_JSAPI_TICKET = ticket;
                JSAPI_TICKET_EXPIRES = new Timestamp(now.getTime() + expires * 1000);
                CACHED = true;
                
                // 更新数据库Cache
                WechatCache cache = new WechatCache(null, null,
                        ticket, new Timestamp(now.getTime() + expires * 1000), null, null);
                wechatCacheService.saveOrUpdateJsapiTicket(cache);
            }
        }
        
        return ticket;
    }
    
    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }
    
    private static String create_nonce_str() {
        return UUID.randomUUID().toString();
    }

    private static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }


    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
    
}
